
# 变量
# 变量的命名字可以包含字符, 数字, 下划线(可以是数字开头)但不应该含有 : # = 或是空字符(空格回车等)
# 变量是大小写敏感的
# 在使用时，需要给在变量名前加上 $ 符号, 但最好用小括号 () 或是大括号 {} 把变量给包括起来
foo = c
prog.o : prog.$(foo)
	$(foo)$(foo) -$(foo) prog.$(foo)











# 变量中的变量
# 在定义变量的值时, 我们可以使用其它变量来构造变量的值, 
# 等于号=定义变量是可以使用后面的变量来定义
foo = $(bar)
bar = $(ugh)
ugh = Huh?
all:
	echo $(foo)
# make all将会打出变量 $(foo) 的值是 Huh?

# 递归定义
A = $(B)
B = $(A)
# 这会让 make 陷入无限的变量展开过程中去, 当然 make 是有能力检测这样的定义, 并会报错











# 赋值符号
# = 是延迟赋值运算符
# 它会在变量被使用时才进行赋值
# 也就是说变量的值将会是整个 Makefile 中最后被指定的值
# := 是立即赋值运算符
# 它会在变量被定义时就进行赋值
# 也就是说变量的值将会是它在 Makefile 中的位置时的值
# (前面的变量不能使用后面的变量, 只能使用前面已定义好了的变量)
x = abc
y := $(x) bar
x = foo
# := y的值是"abc bar", 而不是"foo bar"
x = abc
y = $(x) bar
x = foo
# = y的值是"foo bar", 而不是"abc bar" 
# (y在后面使用时, x最后状态是foo, 即 $(x) 并不会立刻取值, 而是等待 y 被使用时展开)

ifeq (0,${MAKELEVEL})
	cur-dir := $(shell pwd)
	whoami := $(shell whoami)
	host-type := $(shell arch)
	MAKE := ${MAKE} host-type=${host-type} whoami=${whoami}
endif
# 对于系统变量"MAKELEVEL"
# 如果我们的make有一个嵌套执行的动作, 那么这个变量会记录了我们的当前Makefile的调用层数

# dir 这个变量的值是“/foo/bar”, 后面还跟了 4 个空格
dir := /foo/bar    # directory to put the frobs in


# 还有一个比较有用的操作符是 ?=
FOO ?= bar
# 其含义是如果 FOO 没有被定义过, 那么变量 FOO 的值就是"bar"
# 如果 FOO 先前被定义过, 那么这条语将什么也不做, 等价于
ifeq ($(origin FOO), undefined)
	FOO = bar
endif









# 变量的高级用法
# 第一种是变量值的替换
foo := a.o b.o c.o
bar := $(foo:.o=.c)  # 把 $(foo) 中所有以 .o 字串"结尾"全部替换成 .c
# 或者以"静态模式"定义的
foo := a.o b.o c.o
bar := $(foo:%.o=%.c)

# 第二种高级用法是"把变量的值再当成变量"
x = y
y = z
a := $($(x))  # $(a) 的值就是z

# 综合举例 1:
x = variable1
variable2 := Hello
y = $(subst 1,2,$(x))
z = y
a := $($($(z)))  # $(a) 的值就是 Hello

# 举例 2:
first_second = Hello
a = first
b = second
all = $($a_$b)  # $(all) 的值就是 Hello

# 举例 3:
a_objects := a.o b.o c.o
1_objects := 1.o 2.o 3.o
sources := $($(a1)_objects:.o=.c)
# 如果$(a1)的值是"a"的话, $(sources)的值就是"a. c b. c c. c"
# 如果$(a1)的值是"1", 那么$(sources)的值是"1. c2. c3. c"

# 举例 4:
ifdef do_sort
	func := sort
else
	func := strip
endif
bar := a d b g q c
foo := $($(func) $(bar))
# 如果定义了"do_sort", 那么: foo : =$(sort a d b g q c), 于是$(foo)的值就是"a b c d g q"
# 而如果没有定义"do_sort", 那么: foo : =$(strip a d b g q c), 调用的就是strip函数

# 举例 5:
# "把变量的值再当成变量"这种技术, 同样可以用在操作符的左边:
dir = foo
$(dir)_sources := $(wildcard $(dir)/*.c)
define $(dir)_print
	lpr $($(dir)_sources)
endef










# 变量值的追加
objects = main.o foo.o bar.o utils.o
objects += another.o
# $(objects)值变成: "main. o foo. o bar. o utils. o another. o"(another. o被追加进去了)

# 使用 += 操作符可以模拟为下面的这种例子:
objects = main.o foo.o bar.o utils.o
objects := $(objects) another.o


# 如果变量之前没有定义过, += 会自动变成 =, 如果前面有变量定义, += 会继承于前次操作的赋值符
# 如果前一次的是 := 那么 += 会以 := 作为其赋值符
variable := value
variable += more
# 等价于
variable := value
variable := $(variable) more









# override
# 用于强制覆盖先前定义的变量或规则
# 不管之前是否已经定义了相同的变量, 都应该使用当前的定义

override a = 1
default:
	@echo "a = ${a}"
# 在override时, 假设命令行中 make a=2, 但是仍然会输出 a = 1









# 多行变量
# define指示符后面跟的是变量的名字, 而重起一行定义变量的值, 定义是以endef关键字结束
# 其工作方式和 = 操作符一样
# 变量的值可以包含函数, 命令, 文字, 或是其它变量
# 因为命令需要以[Tab]键开头, 所以如果你用define定义的命令变量中没有以Tab键开头, 那么make就不会把其认为是命令.
define two-lines
echo foo
echo $(bar)
endef










# 目标变量
# Makefile 中定义的变量都是"全局变量"
# 当然也同样可以为某个目标设置局部变量
# 它的作用范围只在这条规则以及连带规则中
<target ...> : <variable-assignment>;
<target ...> : overide <variable-assignment>
# <variable-assignment> 可以是前面讲过的各种赋值表达式如 =  :=  +=  或是  ?=

prog : CFLAGS = -g
prog : prog.o foo.o bar.o
	$(CC) $(CFLAGS) prog.o foo.o bar.o
prog.o : prog.c
	$(CC) $(CFLAGS) prog.c
foo.o : foo.c
	$(CC) $(CFLAGS) foo.c
bar.o : bar.c
	$(CC) $(CFLAGS) bar.c
# 不管全局的 $(CFLAGS)的值是什么
# 在prog目标, 以及其所引发的所有规则中(prog. o foo. o bar. o的规则), $(CFLAGS)的值都是-g










# 模式变量
# 变量可以定义在某个目标上
# 模式变量的好处就是, 我们可以给定一种"模式", 可以把变量定义在符合这种模式的所有目标上
# 比如:
%.o : CFLAGS = -O
